package com.smsc.headend.task.engine.executor.impl.dcu;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dto.FtpDataDto;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.data.enums.OperationName;
import com.smsc.headend.module.ftpntp.entity.NtpServer;
import com.smsc.headend.module.fwu.entity.LogFwUpgradeHis;
import com.smsc.headend.module.fwu.entity.TemplateFwUpgrade;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.module.template.dcu.entity.TemplateParamsDcuAutoConnect;
import com.smsc.headend.module.template.dcu.entity.TemplateParamsDcuBasic;
import com.smsc.headend.module.template.dcu.entity.TemplateParamsDcuEthernet;
import com.smsc.headend.module.template.dcu.entity.TemplateParamsDcuGprs;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.dcuparam.SetDcuParamSuccessHandler;
import com.smsc.headend.task.engine.mapper.TemplateDcuAutoCollSchemeMapper;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import com.smsc.headend.task.engine.utils.StepKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author xiaosc
 * @date 2020/11/25
 */
@Slf4j
public class DcuWriteDataExecutor implements TaskExecutor {
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    TemplateDcuAutoCollSchemeMapper templateDcuAutoCollSchemeMapper;
    @Autowired
    TaskDAO taskDao;
    @Autowired
    AssetService assetService;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        log.info("execute step Dcu WriteData Task");
        Map<String, Object> paramMap = this.getParameterMap(task, atomicTask);
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());

        ComEntity terminal = assetService.getComById(task.getComId());
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());

        List<DataItemDTO> dataItemDTOList = dataItemFacade.getDataItemsByOperation(null, terminal, dictionary, getParameterMap(task, atomicTask), comConfigDMO.getModelId());
        if (CollectionUtils.isEmpty(dataItemDTOList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }
        DeviceOperation operationGroup = assetService.getDictionary(atomicTask.getGroupId(), DictionaryType.OperationGroup.getType());
        byte[] packets = getPacketByOperationGroup(atomicTask, comConfigDMO, operationGroup, dataItemDTOList, paramMap);
        if (packets == null) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.PACKETS_IS_NULL.toString());
        }
        log.info(ByteUtils.byteToHexString(packets));
        connectorService.sendPacket(task, atomicTask, dataItemDTOList.get(0).getRoleId(), TaskType.FirmwareFtpSetting.toString().equals(task.getTaskType()) ? true : task.getIsODR(), packets);

        return createAtomicState(getHandlerClass(atomicTask));

    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        if (atomicTask.getGroupId() == OperationGroup.DcuSetBasicParam.getId().intValue()
                || atomicTask.getGroupId() == OperationGroup.DcuSetNtpParam.getId().intValue()
                || atomicTask.getGroupId() == OperationGroup.DcuSetCollScheme.getId().intValue()) {
            String stepOperationName = StepKeyUtil.getStepKey(atomicTask, task);
            taskDao.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), stepOperationName, String.format("[%s]", message), 3600l, false);
        } else if (atomicTask.getGroupId() == OperationGroup.DCUFtpSettings.getId().intValue()
                || atomicTask.getGroupId() == OperationGroup.MeterFtpSettings.getId().intValue()
                || atomicTask.getGroupId() == OperationGroup.MModUpgradeSettings.getId().intValue()
                || atomicTask.getGroupId() == OperationGroup.SModUpgradeSettings.getId().intValue()) {
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            Long taskId = MapUtil.getLong(parameterMap, "taskId");
            Long tvEnd = SystemClock.now() / 1000;
            if(atomicTask.getGroupId() == OperationGroup.DCUFtpSettings.getId().intValue() || atomicTask.getGroupId() == OperationGroup.MModUpgradeSettings.getId().intValue()) {
                ComEntity com = assetService.getComById(task.getComId());
                if(com != null) {
                    assetManagementRemoteService.saveFwuReport(com.getDeviceId(), DeviceType.DCU.getId(), null, taskId, TemplateFwUpgrade.TaskType.FtpSettings.getCode(), LogFwUpgradeHis.UpgradeResult.failed.getCode(), null, tvEnd, message);
                }
            } else {
                String meterIdsStr = MapUtil.getStr(parameterMap, "meterIds");
                String[] meterIds = meterIdsStr.split(",");
                for (String meterId : meterIds) {
                    assetManagementRemoteService.saveFwuReport(Convert.toLong(meterId), DeviceType.METER.getId(), null, taskId, TemplateFwUpgrade.TaskType.FtpSettings.getCode(), LogFwUpgradeHis.UpgradeResult.failed.getCode(), null, tvEnd, message);
                }
            }
        }
        return true;
    }

    private byte[] getPacketByOperationGroup(AtomicTask atomicTask, ComConfigDMO comConfigDMO, DeviceOperation operationGroup, List<DataItemDTO> dataItemDTOList, Map<String, Object> paramMap) throws UDISTaskExecutorException {
        byte[] packet = null;
        OperationGroup operationGp = OperationGroup.forId(operationGroup.getOptId().intValue());
        switch (operationGp) {
            case DcuSetBasicParam:
            case DCUFtpSettings:
            case MeterFtpSettings:
            case MModUpgradeSettings:
            case SModUpgradeSettings:
                packet = dcuSetBasicParamPacket(atomicTask, comConfigDMO, dataItemDTOList, paramMap);
                break;
            case DcuSetNtpParam:
                packet = dcuSetNtpParamPacket(atomicTask, comConfigDMO, dataItemDTOList, paramMap);
                break;
        }
        return packet;
    }

    private byte[] dcuSetBasicParamPacket(AtomicTask atomicTask, ComConfigDMO comConfigDMO, List<DataItemDTO> dataItemDTOList, Map<String, Object> paramMap) throws UDISTaskExecutorException {
        byte[] packet = null;
        switch (OperationName.forId(atomicTask.getOperationId())) {
            case SetDcuClockOverThreshold:
                Long clockOverThreshold = Long.valueOf(String.valueOf(paramMap.get("clockOverThreshold")));
                packet = packetService.setDCUSimpleData(clockOverThreshold, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuMonthOverThreshold:
                Long monthOverThreshold = Long.valueOf(String.valueOf(paramMap.get("monthOverThreshold")));
                packet = packetService.setDCUSimpleData(monthOverThreshold, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuBroadcastTimeCheck:
                String broadcastTimeCheck = String.valueOf(paramMap.get("broadcastTimeCheck"));
                log.debug("broadcastTime {}", broadcastTimeCheck);
                Date date = Convert.toDate(broadcastTimeCheck);
                packet = packetService.setDCUSimpleData(date, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuGPRSParam:
                TemplateParamsDcuGprs templateParamsDcuGprs = JSONUtil.toBean((String) paramMap.get("templateParamsDcuGprs"), TemplateParamsDcuGprs.class);
                packet = packetService.setDCUGPRSParamData(templateParamsDcuGprs, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuEthernetParam:
                TemplateParamsDcuEthernet templateParamsDcuEthernet = JSONUtil.toBean((String) paramMap.get("templateParamsDcuEthernet"), TemplateParamsDcuEthernet.class);
                packet = packetService.setDCUEthernetParamData(templateParamsDcuEthernet, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuBasicComParam:
                TemplateParamsDcuBasic templateParamsDcuBasic = JSONUtil.toBean((String) paramMap.get("templateParamsDcuBasic"), TemplateParamsDcuBasic.class);
                packet = packetService.setDCUBasicParamData(templateParamsDcuBasic, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuLpDensity:
                Long lpDensity = Long.valueOf(String.valueOf(paramMap.get("lpDensity")));
                packet = packetService.setDCUSimpleData(lpDensity, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuReboot:
                packet = packetService.setDCUSimpleData(1, dataItemDTOList.get(0), comConfigDMO);
                break;
            case FtpParametersSet:
                FtpDataDto ftpDataDto = JSONUtil.toBean((String) paramMap.get("ftpDataDto"), FtpDataDto.class);
                packet = packetService.setDCUFtpParamData(ftpDataDto, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetGprsModemApn:
                String gprsModemApn = String.valueOf(paramMap.get("gprsModemApn"));
                gprsModemApn = Convert.toHex(gprsModemApn, StandardCharsets.UTF_8).toUpperCase();
                packet = packetService.setDCUSimpleData(gprsModemApn, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuAutoConnect:
                TemplateParamsDcuAutoConnect autoConnect = JSONUtil.toBean((String) paramMap.get("autoConnect"), TemplateParamsDcuAutoConnect.class);
                packet = packetService.setDcuAutoConnect(autoConnect, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetHeartBeatCycle:
                Long heartBeat = Long.valueOf(String.valueOf(paramMap.get("heartBeat")));
                packet = packetService.setDCUSimpleData(heartBeat, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetAutoRegisterMode:
                Integer autoRegisterMode = Integer.valueOf(String.valueOf(paramMap.get("autoRegisterMode")));
                packet = packetService.setDCUSimpleData(autoRegisterMode, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetRefreshMeterStatus:
                Integer refreshMeterStatus = Integer.valueOf(String.valueOf(paramMap.get("refreshMeterStatus")));
                packet = packetService.setDCUSimpleData(refreshMeterStatus, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetLocalSecretKey:
                String localSecretKey = String.valueOf(paramMap.get("localSecretKey"));
                packet = packetService.setDCUSimpleData(localSecretKey, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetDcuPushStatus:
                Integer pushStatus = Integer.valueOf(String.valueOf(paramMap.get("pushStatus")));
                packet = packetService.setDCUSimpleData(pushStatus, dataItemDTOList.get(0), comConfigDMO);
        }
        return packet;
    }

    private byte[] dcuSetNtpParamPacket(AtomicTask atomicTask, ComConfigDMO comConfigDMO, List<DataItemDTO> dataItemDTOList, Map<String, Object> paramMap) throws UDISTaskExecutorException {
        byte[] packet = null;
        switch (OperationName.forId(atomicTask.getOperationId())) {
            case SetNtpActivated:
                NtpServer ntpServer = JSONUtil.toBean((String) paramMap.get("ntpServerParam"), NtpServer.class);
                packet = packetService.setDCUSimpleData(ntpServer.getActivated() == 1, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetNtpServerAddr:
                ntpServer = JSONUtil.toBean((String) paramMap.get("ntpServerParam"), NtpServer.class);
                String serverAddr = ntpServer.getNetAddr().split(":")[0];
                serverAddr = Convert.toHex(serverAddr, StandardCharsets.UTF_8).toUpperCase();
                packet = packetService.setDCUSimpleData(serverAddr, dataItemDTOList.get(0), comConfigDMO);
                break;
            case SetNtpServerPort:
                ntpServer = JSONUtil.toBean((String) paramMap.get("ntpServerParam"), NtpServer.class);
                Long port = Long.parseLong(ntpServer.getNetAddr().split(":")[1]);
                packet = packetService.setDCUSimpleData(port, dataItemDTOList.get(0), comConfigDMO);
                break;
        }
        return packet;
    }

    private Class getHandlerClass(AtomicTask atomicTask) {
        Class cla = null;
        switch (OperationGroup.forId(atomicTask.getGroupId().intValue())) {
            case DcuSetBasicParam:
            case DCUFtpSettings:
            case MeterFtpSettings:
            case MModUpgradeSettings:
            case SModUpgradeSettings:
                cla = getDcuSetBasicParamHandler(atomicTask.getOperationId());
                break;
            case DcuSetNtpParam:
                cla = getDcuNtpParamHandler(atomicTask.getOperationId());
                break;
            default:
                break;
        }
        return cla;
    }

    private Class getDcuSetBasicParamHandler(Long operationId) {
        OperationName operationName = OperationName.forId(operationId);
        switch (operationName) {
            case SetDcuClockOverThreshold:
            case SetDcuMonthOverThreshold:
            case SetDcuBroadcastTimeCheck:
            case SetDcuGPRSParam:
            case SetDcuEthernetParam:
            case SetDcuBasicComParam:
            case SetDcuLpDensity:
            case SetDcuReboot:
            case FtpParametersSet:
            case SetGprsModemApn:
            case SetDcuAutoConnect:
            case SetHeartBeatCycle:
            case SetAutoRegisterMode:
            case SetRefreshMeterStatus:
            case SetLocalSecretKey:
            case SetDcuPushStatus:
                return SetDcuParamSuccessHandler.class;
        }
        return null;
    }

    private Class getDcuNtpParamHandler(Long operationId) {
        OperationName operationName = OperationName.forId(operationId);
        switch (operationName) {
            case SetNtpActivated:
            case SetNtpServerAddr:
            case SetNtpServerPort:
                return SetDcuParamSuccessHandler.class;
        }
        return null;
    }
}
